#!/bin/bash

#
# This file is part of the batocera distribution (https://batocera.org).
# Copyright (c) 2025+.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# YOU MUST KEEP THIS HEADER AS IT IS
#

# batocera-xtract, a script as universal extractor tool and to mimic file-roller base functions
# MimeTypes=application/x-7z-compressed;application/gzip;application/vnd.rar;application/x-tar;application/x-compressed-tar;
#           application/x-xz-compressed-tar;application/x-iso;application/zip;

# inital idea and inspired by brunoeduardobrasil, 07/19/2025 cyperghost aka crcerror
# ec-codes: ec-0 okay, ec-1 error, ec-10 parameter l used
# ec-11 archives not supported for extraction, ec-12 archives not supported for showing content
# ec-21 to 29 -- file extraction error zip rar 7z gz iso tar tar.xz tar.gz exe (only InnoSetup)

# Is pcmanfm is active then use yad as output. test -t will not work because pcmanfm creates a virtual terminal
# SSH_CONNECTION and SSH_TTY are active if SSH connection is used
pgrep pcmanfm >/dev/null && [[ -z "$SSH_CONNECTION" ]] && TERMINAL=0 || TERMINAL=1
ret=0
# get path to 7z binary... the so lib is borked, can't use alias 'which'
bin7z=$(command -v 7z)

list_archive () {
    # We obtain the file names, dirs, date+time and size with minimal filters, unzip -Z1, unrar lb, 7zr l -ba ... and some awk magic
    # 7z and iso play again the rules and use empty strings for dirs or uncompressed files, so NF changes and we need to check if there are digits
    # overwrite of old file os 7z -y, unrar -o+ and tar does it always
    case "$1" in
        *.zip) readarray -t array < <($bin7z l -ba "$FILE"  | awk '{ print "FALSE"; for(i=6; i<NF; ++i) printf $i" "; print $NF; if ($3 ~ /^D/) print "DIR"; else print $4; print $1,$2; }') ;;
        *.iso) readarray -t array < <($bin7z l -ba "$FILE"  | awk '{ print "FALSE"; for(i=4; i<NF; ++i) if ($i ~ /^[0-9]+$/) continue; else printf $i" "; print $NF; if ($3 ~ /^D/) print "DIR"; else print $4; print $1,$2; }') ;;
        *.rar) readarray -t array < <(printf "FALSE\n%s\nna\nna\n" $(unrar lb "$FILE")) ;;
        *.7z)  readarray -t array < <($bin7z l -ba "$FILE" | awk '{ print "FALSE"; for(i=5; i<NF; ++i) if ($i ~ /^[0-9]+$/) continue; else printf $i" "; print $NF; if ($3 ~ /^D/) print "DIR"; else print $4; print $1,$2; }') ;;
        *.tar) readarray -t array < <(tar -tvf "$FILE"  | awk '{ print "FALSE"; for(i=6; i<NF; ++i) printf $i" "; print $NF; if ($1 ~ /^d/) print "DIR"; else print $3; print $4, $5; }') ;;
        *.tar.gz|*.tgz) readarray -t array < <(tar -tvzf "$FILE" | awk '{ print "FALSE"; for(i=6; i<NF; ++i) printf $i" "; print $NF; if ($1 ~ /^d/) print "DIR"; else print $3; print $4, $5; }') ;;
        *.gz) readarray -t array < <($bin7z l -ba "$FILE"  | awk '{ print "FALSE"; for(i=6; i<NF; ++i) printf $i" "; print $NF; if ($3 ~ /^D/) print "DIR"; else print $4; print $1,$2 }') ;;
        # Unlike gzip is xy only a compression format for a single file, so always open with tar extension and do not use the single compression types
        *.tar.xz) readarray -t array < <(tar -tvJf "$FILE" | awk '{ print "FALSE"; for(i=6; i<NF; ++i) printf $i" "; print $NF; if ($1 ~ /^d/) print "DIR"; else print $3; print $4, $5; }') ;; 
        *) return 12
    esac

    # Just output for CLI as list - usefull for further scripting
    [[ "${2}" == "l" ]] && { printf '%s\t%s\t%s%s\n' "${array[@]}" | awk -F'\t' '{ print $2 }'; return 10; }

    #Options needs to be added to PCmanFM "file-roller --open"
    readarray -t files <<< $(yad --title="Extract Files" --list --checklist --print-column=2 --separator= --column=Extract:chk --column=Filename:text --column=Size:text --column=Date:text "${array[@]}")
    [[ -z "${files[0]}" ]] && return 1 || return 0 # No file ticked or Cancel pressed
}

extract_archive () {
    case  "$1" in
        *.zip) $bin7z x -y "$FILE" -o"$DEST" "${files[@]}" || return 21 ;;
        *.rar) unrar x -o+ "$FILE" "${files[@]}" "$DEST/" || return 22 ;;
        *.7z)  $bin7z x -y "$FILE" -o"$DEST" "${files[@]}" || return 23 ;;
        *.iso) $bin7z x -y "$FILE" -o"$DEST" "${files[@]}" || return 25 ;;
        *.tar) tar -xf "$FILE" "${files[@]}" -C "$DEST" || return 26 ;;
        *.tar.xz) tar -xJf "$FILE" "${files[@]}" -C "$DEST" || return 27 ;;
        *.tar.gz|*.tgz) tar -xzf "$FILE" "${files[@]}" -C "$DEST" || return 28 ;;
        *.gz)  $bin7z x -y "$FILE" -o"$DEST" "${files[@]}" || return 24 ;;
        *.exe) innoextract -e -L -m "$FILE" -d "$DEST" || return 29 ;;
        *) return 11
    esac
}

case "$1" in
    x|X|clix) #for CLI as universal extraction tool
        FILE="$2"; DEST="$3"
        [[ -f "$FILE" ]] || { echo "Error! Archive '$FILE' not found"; exit 1; }
        [[ "$1" == "clix" && -z "$DEST" ]] && DEST="$PWD"
        if [[ -z "$DEST" ]]; then
            FILENAME="$(basename "${FILE%.*}")"
            echo "No Destination directory entered!"
            echo "C or 1) to extract to CURRENT: '$PWD'"
            echo "B or 2) to extract to BASEDIR: '$FILENAME' in '$PWD'"
            read -p "Select (C/B) and press Enter: " yn
            case ${yn:0:1} in
                c|C|1) DEST="$PWD" ;;
                b|B|2) mkdir "$FILENAME"; DEST="${PWD}/${FILENAME}" ;;
                *)   echo "...Aborted!"; exit 1
            esac
        fi
        [[ -d "$DEST" ]] && extract_archive "${FILE,,}" || ret=3
    ;;
    --extract)
        FILE="$2"
        DEST=$(yad --title "Choose destination folder" --file --directory) || exit 1
        extract_archive "${FILE,,}" || ret=$?
    ;;
    --extract-to)
        FILE="$3"
        #from https://askubuntu.com/questions/53770/how-can-i-encode-and-decode-percent-encoded-strings-on-the-command-line --> URI 2 filenamespace
        DEST="${2/file:\/\/}"; DEST=$(printf '%b' "${DEST//%/\\x}")
        extract_archive "${FILE,,}" || ret=$?
    ;;
    --open|open|l|L)
        FILE="$2"
        DEST="$(readlink -f "$(dirname "${FILE}")")"
        list_archive "$(basename "${FILE,,}")" "${1,,}" && extract_archive "$(basename "${FILE,,}")" || ret=$?
    ;;
    --add)
        # We remove $0 and $1, create filename.zip if single dir/file, create subdirname.zip for several files/dirs, we need the work-dir to have the relative pathes
        FILE=("${@:2}")
        DEST="${FILE%/*}"
        [[ -n "${DEST}" ]] && { cd "${DEST}" || { yad --title "Error" --text "Can't open directory '$DEST'"; exit 1; }; } || { DEST="/root"; cd /; }
        [[ ${#FILE[@]} -gt 1 ]] && { DEST="${DEST}/$(basename "${DEST}")"; }
        [[ ${#FILE[@]} -eq 1 ]] && { DEST="$(basename "${FILE%.*}")"; }
        [[ -f "${DEST}.zip" ]] && DEST="${DEST}_$(printf '%x' $(date +%s)).zip" || DEST="${DEST}.zip"
        FILE=("${FILE[@]/*\/}")   #strip pathes to avoid to build archives with absolue pathes
        zip -q -r "$DEST" "${FILE[@]}" && { yad --title "Created zip" --text "Created '$(readlink -f $DEST)' and added ${#FILE[@]} files:\n$(printf '%s\n' "${FILE[@]}")"; exit 0; } || exit 1
    ;;
   *) ret=2
esac

case $ret in
    0|1) true ;;
    2) # Action not defined
        [[ $TERMINAL -eq 0 ]] && { yad --title "Error" --text "Action: '$1' is unknown"; exit 2; }
        echo "Usage: $(basename "$0") <SWITCHES> [ARCHIVEFILE] {DESTINATION-DIR}"; echo
        echo "    x: extraction of current archivfile"
        echo " clix: extraction of current archive for without prompt (CLI)"
        echo "    l: list archive content"; echo
        echo "Supported archives for extraction: zip 7z rar tar gz iso tar.gz/tgz tar.xz exe"
        echo "Supported archives for list/open : zip 7z rar tar gz iso tar.gz/tgz tar.xz"; echo
        echo "error-codes/return codes:"
        echo "  ec-0 no error, ec-10 parameter 'l', okay -- ec-1 general error -- ec-2 unknown action"
        echo "  ec-11 to 12 -- archive type not supported for extraction, listing"
        echo "  ec-21 to 28 -- file extraction error zip rar 7z gz iso tar tar.xz tar.gz"
        echo "  ec-29       -- file extraction error for exe-files (InnoSetup)" ;echo
    ;;
    3)  echo "Error! Destionation directory '$DEST' not found" ;; #CLI Destination dir not found
    11|12) # Extension not supported for extraction or listing
        [[ $TERMINAL -eq 0 ]] && { yad --title "Error" --text "Archive type: '${FILE##*.}' is not yet supported.\nFailed to open '$FILE'"; }
        [[ $TERMINAL -eq 1 ]] && { echo "Error: Archive type: '${FILE##*.}' is not yet supported. Failed to open '$FILE'"; }
    ;;
   2[1-9]) # Error in extraction
        [[ $TERMINAL -eq 0 ]] && { yad --title "Error" --text "Archive type: '${FILE##*.}' Extraction error: $ret\nFailed to extract '$FILE'"; }
        [[ $TERMINAL -eq 1 ]] && { echo "Error: Archive type: '${FILE##*.}' Extraction error - Failed to extract '$FILE'"; }
    ;;
esac
echo "Exit with code: $ret"
exit $ret
